// 自执行函数
function co(fn) {
    let gen = fn()
    function next(params) {
        let { value, done } = gen.next(params)
        if (done) return value
        if (value.then) {
            value.then(res => next(res))
        } else {
            next(value)
        }
    }
    next()
}

// promise 实现
class MyPromise {
    constructor(fn) {
        this.resolvedCallbacks = [];
        this.rejectedCallbacks = [];
        this.state = "PENDING"; // PENDING REDOLVED REJECTED
        this.value = "";
        fn(this.resolve.bind(this), this.reject.bine(this));
    }
    resolve(value) {
        if (this.state == "PENDING") {
            this.state = "RESOLVED";
            this.value = value;
            this.resolvedCallbacks.map(cb => cb(value))
        }
    }
    reject(value) {
        if (this.state == "PENDING") {
            this.state = "REJECTED";
            this.value = value;
            this.rejectedCallbacks.map(cb => cb(value))
        }
    }
    then(onFulfilled, onRejected) {
        if(this.state == "PENDING" ){
            this.resolvedCallbacks.push(onFulfilled)
            this.rejectedCallbacks.push(onRejected)
        }
        if(this.state == "RESOLVED"){
            onFulfilled(this.value)
        }
        if(this.state == "REJECTED"){
            onRejected(this.value)
        }

    }
}
// 具体的Promise A+规范请阅读 v1详细版本



// 这里也是一种发布订阅
// 实现一个Queue函数，调用start之后，1s后打印1，接着2s后打印2，然后3s后打印3
class Queue {
    constructor() {
        this.taskList = [];
    }
    task(delay, fn) {
        this.taskList.push(() => sleep(delay, fn))
        return this
    }
    start() {
        this.taskList.forEach(async (fn) => {
            await fn()
        })
    }
}

function sleep(delay, fn) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(fn())
        }, delay)
    })
}
// new Queue()
//     .task(1000, () => console.log(1))
//     .task(2000, () => console.log(2))
//     .task(3000, () => console.log(3)).start();


// 变种 lazyMan
// LazyMan('Tony').eat('lunch').eat('dinner').sleepFirst(5).sleep(10).eat('junk food');
// Hi I am Tony
// 等待了5秒...
// I am eating lunch
// I am eating dinner
// 等待了10秒...
// I am eating junk food
class lazyManClass {
    constructor(name) {
        this.name = name;
        this.taskList = [];
        console.log(`nihao woshi ${name}`)
        setTimeout(() => {
            this.next()
        }, 0)
    }

    eat(name) {
        let that = this
        let fn = function () {
            console.log(`I am eating ${name}`)
            that.next()
        }
        this.taskList.push(fn)
        return this
    }

    // 保证最先执行
    sleepFirst(time) {
        let that = this
        let fn = function () {
            setTimeout(() => {
                console.log(`等待了${time}秒...`)
                that.next()
            }, time*1000)
        }
        this.taskList.unshift(fn)
        return this

    }

    sleep(time) {
        let that = this;
        let fn = function () {
            setTimeout(() => {
                console.log(`等待了${time}秒...`)
                that.next()
            }, time*1000)
        }
        this.taskList.push(fn)
        return this
    }
    next() {
        var fn = this.taskList.shift()
        fn && fn()
    }
}

function LazyMan(name) {
    return new lazyManClass(name)
}

LazyMan('Tony').eat('lunch').eat('dinner').sleepFirst(5).sleep(10).eat('junk food');

// promise 方法实现
Promise.prototype.myAll = function (...args) {
    let result = []
    new Promise(function (resolve, reject) {
        for (let i = 0; i < args.length; i++) {
            args[i]().then(res => {
                result[i] = res // 优先
                if (result.length == args.length) {
                    return resolve(result)
                }
            }).catch(err => {
                reject(err)
            })
        }
    }, function (err) {
        reject(err)
    })
}

Promise.prototype.myRace = function (...args) {
    new Promise(function (resolve, reject) {
        for (let i = 0; i < args.length; i++) {
            args[i]().then(res => {
                return resolve(res)
            }).catch(err => {
                reject(err)
            })
        }
    }, function (err) {
        reject(err)
    })
}


Promise.prototype.finally = function (callback) {
    let P = this.constructor;
    return this.then(
      value  => P.resolve(callback()).then(() => value),
      reason => P.resolve(callback()).then(() => { throw reason })
    );
  };